package com.bestcem.xm.survey.grpc.service;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.bestcem.xm.common.core.exception.BusinessException;
import com.bestcem.xm.deliver.grpc.v1.services.DeliverSurvey;
import com.bestcem.xm.survey.entity.SurveyRespondent;
import com.bestcem.xm.survey.grpc.client.DeliverGrpcClient;
import com.bestcem.xm.survey.grpc.v1.services.*;
import com.bestcem.xm.survey.mapper.SurveyRespondentMapper;
import com.bestcem.xm.survey.service.ConditionAnalysisService;
import com.bestcem.xm.survey.service.SurveyColumnService;
import com.google.common.collect.Lists;
import io.grpc.stub.StreamObserver;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.server.service.GrpcService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import zhongyan.xm.survey.v1.enums.MatchTypeOuterClass;

import java.util.*;
import java.util.stream.Collectors;

import static com.bestcem.xm.survey.enums.RespondentDataStatusEnum.RESPONDENT_DATA_STATUS_FORMAL;
import static com.bestcem.xm.survey.enums.RespondentStatusEnum.*;
import static com.bestcem.xm.survey.grpc.service.GrpcConstant.FAIL_STATUS;
import static com.bestcem.xm.survey.grpc.service.GrpcConstant.SUCCESS_STATUS;
import static com.bestcem.xm.survey.util.SurveyConstant.RESPONDENT_STATUS_DICT;
import static zhongyan.xm.survey.v1.enums.MatchTypeOuterClass.MatchTypeEnum.MatchType.*;

/**
 * 获取基础survey接口
 *
 * @author xiaoshu.shao <xiaoshu.shao@idiaoyan.com>
 * @version V1.0
 * @date 2022/9/21
 */
@GrpcService
@Slf4j
@RequiredArgsConstructor
public class BpSurveyGrpcService extends BpSurveyServiceGrpc.BpSurveyServiceImplBase {
    // 联系人取答卷数据 分段读取
    private final static int MEMBER_ID_PART = 30;
    private @NonNull SurveyRespondentMapper surveyRespondentMapper;
    private @NonNull ConditionAnalysisService conditionAnalysisService;
    private @NonNull SurveyColumnService surveyColumnService;
    private @NonNull DeliverGrpcClient deliverGrpcClient;

    @Override
    public void getSurveyScoreBySeq(GetSurveyScoreBySeqRequest request, StreamObserver<GetSurveyScoreBySeqResponse> responseObserver) {
        log.info("[BpSurveyGrpcService] now getSurveyScoreBySeq");
        GetSurveyScoreBySeqResponse.Builder response = GetSurveyScoreBySeqResponse.newBuilder();

        int seq = request.getSeq();
        String projectId = request.getProjectId();
        if (StringUtils.isEmpty(projectId)) {
            response.setStatus(FAIL_STATUS);
            responseObserver.onNext(response.build());
            responseObserver.onCompleted();
            return;
        }
        JSONObject js = new JSONObject();
        js.put("seq", seq);
        js.put("projectId", projectId);
        SurveyRespondent surveyRespondent = surveyRespondentMapper.selectOne(
                new QueryWrapper<SurveyRespondent>().lambda().eq(SurveyRespondent::getProjectId, projectId)
                        .eq(SurveyRespondent::getSeq, seq).last("limit 1"));
        if (ObjectUtil.isNull(surveyRespondent)) {
            response.setStatus(FAIL_STATUS);
            responseObserver.onNext(response.build());
            responseObserver.onCompleted();
            return;
        }
        js.put("version", surveyRespondent.getVersion());

        js.put("id", surveyRespondent.getId());
        js.put("status", surveyRespondent.getStatus());

        Double score = surveyColumnService.getScoreByAnswer(js);
        if (score != null) {
            response.setScore(String.valueOf(score.doubleValue()));
        }
        response.setStatus(SUCCESS_STATUS);
        responseObserver.onNext(response.build());
        responseObserver.onCompleted();
    }

    @Override
    public void getMemberIdBySeq(GetMemberIdBySeqRequest request, StreamObserver<GetMemberIdBySeqResponse> responseObserver) {
        log.info("[BpSurveyGrpcService] now getMemberIdBySeq");
        GetMemberIdBySeqResponse.Builder response = GetMemberIdBySeqResponse.newBuilder();

        int seq = request.getSeq();
        String projectId = request.getProjectId();
        if (StringUtils.isEmpty(projectId)) {
            response.setStatus(FAIL_STATUS);
            responseObserver.onNext(response.build());
            responseObserver.onCompleted();
            return;
        }
        SurveyRespondent surveyRespondent = surveyRespondentMapper.selectOne(
                new QueryWrapper<SurveyRespondent>().lambda().eq(SurveyRespondent::getProjectId, projectId)
                        .eq(SurveyRespondent::getSeq, seq).last("limit 1"));
        if (ObjectUtil.isNull(surveyRespondent)) {
            response.setStatus(FAIL_STATUS);
            responseObserver.onNext(response.build());
            responseObserver.onCompleted();
            return;
        }
        response.setMemberId(surveyRespondent.getMemberId());
        response.setStatus(SUCCESS_STATUS);
        responseObserver.onNext(response.build());
        responseObserver.onCompleted();
    }

    @Override
    public void executeConditions(ExecuteConditionsRequest request, StreamObserver<ExecuteConditionsResponse> responseObserver) {
        log.info("[BpSurveyGrpcService] now executeConditions");

        List<ExecuteCondition> conditions = request.getConditionsList();
        // 优化方案， 传入的参数中project_id重复的较多，以project_id分组查询
        Map<String, List<String>> pmData = new HashMap<>();
        for (ExecuteCondition condition : conditions) {
            if (pmData.containsKey(condition.getProjectId())) {
                pmData.get(condition.getProjectId()).add(condition.getMemberId());
            } else {
                List<String> memberList = new ArrayList<>();
                memberList.add(condition.getMemberId());
                pmData.put(condition.getProjectId(), memberList);
            }
        }
        JSONObject res = getEffectiveNewestRspdV2(pmData);

        List<ExecuteConditionResult> results = new ArrayList<>();
        for (ExecuteCondition conditionInfo : conditions) {
            String conditionId = conditionInfo.getConditionId();
            // 获取最新完成答卷的seq
            // res = await get_effective_newest_rspd(condition_info["project_id"], condition_info["member_id"])
            // if not res or not res.seq:
            MatchTypeOuterClass.MatchTypeEnum.MatchType match;
            if (res.get(conditionInfo.getProjectId() + "_" + conditionInfo.getMemberId()) == null) {
                match = NO_DATA;
            } else {
                // 条件检查
                Long seq = res.getLong(conditionInfo.getProjectId() + "_" + conditionInfo.getMemberId());
                boolean checkResult = conditionAnalysisService.checkNestConditions(seq, conditionInfo.getCondition(), conditionInfo.getProjectId());
                if (checkResult) {
                    match = MATCH;
                } else {
                    match = MISMATCH;
                }
            }
            ExecuteConditionResult.Builder condBuilder = ExecuteConditionResult.newBuilder();
            condBuilder.setMatch(match);
            condBuilder.setConditionId(conditionId);
            condBuilder.setMemberId(conditionInfo.getMemberId());
            condBuilder.setProjectId(conditionInfo.getProjectId());
            results.add(condBuilder.build());
        }
        ExecuteConditionsResponse.Builder response = ExecuteConditionsResponse.newBuilder();
        response.setStatus(SUCCESS_STATUS);
        response.addAllResults(results);
        responseObserver.onNext(response.build());
        responseObserver.onCompleted();
    }

    private JSONObject getEffectiveNewestRspdV2(Map<String, List<String>> pmData) {
        JSONObject result = new JSONObject();
        for (String projectId : pmData.keySet()) {
            List<String> memberIds = pmData.get(projectId);
            for (String memberId : memberIds) {
                result.put(projectId + "_" + memberId, null);
            }
            LambdaQueryWrapper<SurveyRespondent> queryWrapper = new LambdaQueryWrapper<SurveyRespondent>()
                    .eq(SurveyRespondent::getProjectId, projectId)
                    .eq(SurveyRespondent::getStatus, 1)
                    .in(SurveyRespondent::getMemberId, memberIds)
                    .eq(SurveyRespondent::getDataStatus, RESPONDENT_STATUS_FINISH.getValue());
            List<SurveyRespondent> surveyRespondents = surveyRespondentMapper.selectList(queryWrapper.select(SurveyRespondent::getProjectId,
                    SurveyRespondent::getMemberId, SurveyRespondent::getSeq).orderByDesc(SurveyRespondent::getFinishTime));
            for (SurveyRespondent surveyResp : surveyRespondents) {
                result.put(surveyResp.getProjectId() + "_" + surveyResp.getMemberId(), surveyResp.getSeq());
            }
        }
        return result;
    }

    @Override
    public void surveyGroupIsInUse(SurveyGroupIsInUseRequest request, StreamObserver<SurveyGroupIsInUseResponse> responseObserver) {
        String groupId = request.getGroupId();
        LambdaQueryWrapper<SurveyRespondent> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(SurveyRespondent::getGroupId)
                .eq(SurveyRespondent::getGroupId, groupId)
                .eq(SurveyRespondent::getStatus, RESPONDENT_DATA_STATUS_FORMAL.getValue())
                .last("limit 1");
        SurveyRespondent surveyRespondent = surveyRespondentMapper.selectOne(queryWrapper);
        SurveyGroupIsInUseResponse.Builder response = SurveyGroupIsInUseResponse.newBuilder();
        response.setStatus(SUCCESS_STATUS);
        response.setInUse(Objects.nonNull(surveyRespondent));
        responseObserver.onNext(response.build());
        responseObserver.onCompleted();
    }

    @Override
    public void listMemberStats(ListMemberStatsRequest request, StreamObserver<ListMemberStatsResponse> responseObserver) {
        log.info("[BpSurveyGrpcService] now listMemberStats");
        ListMemberStatsResponse.Builder response = ListMemberStatsResponse.newBuilder();

        List<String> memberIds = request.getMemberIdsList();
        String orgId = request.getOrgId();
        if (CollectionUtils.isEmpty(memberIds)) {
            log.error("The member ids is null");
            response.setStatus(FAIL_STATUS);
            responseObserver.onNext(response.build());
            responseObserver.onCompleted();
            return;
        }

        response.addAllStats(respondentRelevantRateCalcMidV2(memberIds, orgId));
        response.setStatus(SUCCESS_STATUS);
        responseObserver.onNext(response.build());
        responseObserver.onCompleted();
    }

    private List<MemberStat> respondentRelevantRateCalcMidV2(List<String> memberIds, String orgId) {
        // 计算所有联系人的答题率
        //    答题率：开始答题的问卷数/收到问卷数*100%
        //    答题完成率：成功完成答卷数/开始答题的问卷数*100%
        // 获取所有投放的问卷数量
        Map<String, List<String>> deliverSurveyData = new HashMap<>();
        List<DeliverSurvey> deliverSurveyList = new ArrayList<>();
        List<List<String>> memberIdList = Lists.partition(memberIds, MEMBER_ID_PART);
        for (List<String> memberIdLs : memberIdList) {
            List<DeliverSurvey> ret = getDeliverSurveyDataForComputeV2(memberIdLs);
            if (CollectionUtils.isNotEmpty(ret)) {
                deliverSurveyList.addAll(ret);
            }
        }

        for (DeliverSurvey deliverSurvey : deliverSurveyList) {
            if (StringUtils.isNotEmpty(deliverSurvey.getDeliverId())) {
                List<String> memberDeliver = deliverSurveyData.get(deliverSurvey.getMemberId());
                if (memberDeliver == null) {
                    memberDeliver = new ArrayList<>();
                }
                memberDeliver.add(deliverSurvey.getDeliverId());
                deliverSurveyData.put(deliverSurvey.getMemberId(), memberDeliver);
            } else {
                List<String> memberDeliver = deliverSurveyData.get(deliverSurvey.getMemberId());
                if (memberDeliver == null) {
                    memberDeliver = new ArrayList<>();
                }
                memberDeliver.add(deliverSurvey.getDeliverGroupId());
                deliverSurveyData.put(deliverSurvey.getMemberId(), memberDeliver);
            }
        }

        List<String> hasDeliverSurveyMembers = new ArrayList<>(deliverSurveyData.keySet());
        List<String> notDeliverSurveyMembers = new ArrayList<>(CollectionUtils.disjunction(memberIds, hasDeliverSurveyMembers));
        for (String member : notDeliverSurveyMembers) {
            deliverSurveyData.put(member, new ArrayList<>());
        }

        List<SurveyRespondent> respondentList = getAllRespondentForComputeV1(memberIds, orgId);
        Map<String, List<SurveyRespondent>> respondentsData = new HashMap<>();
        for (SurveyRespondent res : respondentList) {
            if (respondentsData.get(res.getMemberId()) == null) {
                List<SurveyRespondent> resps = new ArrayList<>();
                resps.add(res);
                respondentsData.put(res.getMemberId(), resps);
            } else {
                respondentsData.get(res.getMemberId()).add(res);
            }
        }
        List<MemberStat> result = new ArrayList<>();
        for (String memberId : memberIds) {
            // 打开未完成的计数
            int openCount = 0;
            int submitCount = 0;
            // 历史答卷中{投放id:该id的答卷数量}
            Map<String, Integer> respondentDeliverToCountMap = new HashMap<>();
            // 历史答卷中{投放id:投放组id}
            Map<String, String> respondentDeliverToDlvGIdMap = new HashMap<>();
            List<SurveyRespondent> respondents = Optional.ofNullable(respondentsData.get(memberId)).orElse(new ArrayList<>());
            List<Integer> openStatus = Arrays.asList(RESPONDENT_STATUS_BEGIN.getValue(), RESPONDENT_STATUS_OPEN.getValue(),
                    RESPONDENT_STATUS_UNFINISH.getValue());
            for (SurveyRespondent respondent : respondents) {
                Integer data = respondentDeliverToCountMap.get(respondent.getDeliverId());
                respondentDeliverToDlvGIdMap.put(respondent.getDeliverId(), respondent.getDeliverGroupId());
                if (data != null) {
                    respondentDeliverToCountMap.put(respondent.getDeliverId(), data + 1);
                } else {
                    respondentDeliverToCountMap.put(respondent.getDeliverId(), 1);
                }
                if (openStatus.contains(respondent.getStatus())) {
                    // 打开问卷 未完成
                    openCount++;
                } else {
                    // 已提交的问卷个数
                    submitCount++;
                }
            }

            // 收到问卷数
            List<String> deliverDataList = deliverSurveyData.get(memberId);
            int receiveTotalCount = 0;
            if (deliverDataList != null) {
                receiveTotalCount = deliverDataList.size();
            }
            // 投放总数
            int deliverTotalCount = receiveTotalCount;
            for (String deliverId : respondentDeliverToCountMap.keySet()) {
                Integer count = respondentDeliverToCountMap.get(deliverId);
                // 如果历史答卷中的问卷deliver 存在于 放记录中 则 投放总数 = 投放记录 + 历史答卷 - 1
                if (deliverSurveyData.get(memberId).contains(deliverId) || deliverSurveyData.get(memberId)
                        .contains(respondentDeliverToDlvGIdMap.get(deliverId))) {
                    deliverTotalCount += count - 1;
                } else {
                    deliverTotalCount += count;
                }
            }
            MemberStat.Builder memberBuilder = MemberStat.newBuilder();
            memberBuilder.setMemberId(memberId);
            // 开始答题的问卷数
            int startAnswerCount = openCount + submitCount;
            // 计算 答题率：开始答题的问卷数/收到问卷数*100%
            if (deliverTotalCount > 0) {
                memberBuilder.setAnswerRate(Math.round((float) startAnswerCount / deliverTotalCount * 100));
            } else {
                memberBuilder.setAnswerRate(0);
            }
            // 答题完成率：成功完成答卷数/开始答题的问卷数*100%
            if (startAnswerCount > 0) {
                memberBuilder.setSubmitRate(Math.round(submitCount / (float) startAnswerCount * 100));
            } else {
                memberBuilder.setSubmitRate(0);
            }
            memberBuilder.setStartAnswerCount(startAnswerCount);
            memberBuilder.setReceiveDeliverCount(receiveTotalCount);
            result.add(memberBuilder.build());
        }
        return result;
    }

    private List<DeliverSurvey> getDeliverSurveyDataForComputeV2(List<String> memberIds) {
        return deliverGrpcClient.getDeliverSurveyV2(memberIds);
    }

    private List<SurveyRespondent> getAllRespondentForComputeV1(List<String> memberIds, String orgId) {
        // 根据组织id，获取所有用户的答卷数据
        // 目前非分表，用不到projectIdList
//        List<String> projectIdList = qdesGrpcClient.getProjectIds(orgId);
        List<Integer> statusList = RESPONDENT_STATUS_DICT.keySet().stream().filter(key ->
                !Arrays.asList(RESPONDENT_STATUS_DELETE.getValue(),
                        RESPONDENT_STATUS_DISCARD.getValue()).contains(key)).collect(Collectors.toList());
        // 1.org_id对应的所有子表
        JSONObject projectIdModelCls = new JSONObject();
        JSONObject projectIdDict = new JSONObject();
        // python中先获取对应分表的子表，然后再获取数据
        List<SurveyRespondent> respondents = surveyRespondentMapper.selectList(new LambdaQueryWrapper<SurveyRespondent>()
                .in(SurveyRespondent::getMemberId, memberIds)
                .eq(SurveyRespondent::getDataStatus, RESPONDENT_DATA_STATUS_FORMAL.getValue())
                .in(SurveyRespondent::getStatus, statusList));
        return respondents;
    }

    @Override
    public void updateRespondentMemberId(UpdateRespondentMemberIdBySeqRequest request, StreamObserver<UpdateResponse> responseObserver) {
        UpdateResponse.Builder response = UpdateResponse.newBuilder();
        String seq = request.getSeq();
        String member_id = request.getMemberId();
        String project_id = request.getProjectId();
        if (StrUtil.isNotEmpty(seq)) {
            String id = updateRespondentBySeq(seq, member_id, project_id);
            response.setStatus(SUCCESS_STATUS);
            response.setId(id);
        } else {
            log.error("答卷更新失败");
            response.setStatus(FAIL_STATUS);
            return;
        }
        responseObserver.onNext(response.build());
        responseObserver.onCompleted();
    }

    private String updateRespondentBySeq(String seq, String memberId, String projectId) {
        LambdaQueryWrapper<SurveyRespondent> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SurveyRespondent::getSeq, Integer.valueOf(seq));
        queryWrapper.eq(SurveyRespondent::getProjectId, projectId);
        queryWrapper.last("limit 1");
        SurveyRespondent surveyRespondent = surveyRespondentMapper.selectOne(queryWrapper);
        if (Objects.isNull(surveyRespondent)) {
            throw new BusinessException("respondent not found");
        }
        LambdaUpdateWrapper<SurveyRespondent> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(SurveyRespondent::getMemberId, memberId).eq(SurveyRespondent::getId, surveyRespondent.getId());
        surveyRespondentMapper.update(null, updateWrapper);
        return surveyRespondent.getId();
    }
}